﻿using System;
using System.Drawing;
using System.Windows.Forms;
using Oxage.Simulator;

namespace Oxage.Controls
{
	public partial class RasterSimulationControl : Control
	{
		#region Private members
		private Bitmap pixel;
		private Graphics g, gBackground, gScreen;
		private BufferedGraphics gBuffer;
		private BufferedGraphics gBufferBackground; //Static background
		private BufferedGraphics gBufferScreen; //Before drawing on screen
		private BufferedGraphicsContext gManager;
		private Timer refreshTimer;
		#endregion

		#region Constructor
		public RasterSimulationControl()
		{
			Rectangle bounds = new Rectangle(0, 0, 1440, 890);
			this.Location = new Point(bounds.X, bounds.Y);
			this.Size = new Size(bounds.Width, bounds.Height);

			this.cursor = new NPoint(0, 0, 0);

			//this.IsGridVisible = true;
			//this.IsCrossVisible = true;
			//this.IsTrackerVisible = true;

			this.BackColor = Color.White;
			this.GridColor = Color.Gray;
			this.CrossColor = Color.Violet;
			this.TrackerColor = Color.Green;

			//Create a pixel
			pixel = new Bitmap(1, 1);
			pixel.SetPixel(0, 0, Color.Red);

			//Optimize painting
			this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);

			//Set refresh timer
			refreshTimer = new Timer();
			refreshTimer.Tick += (sender, e) => Render();
			refreshTimer.Interval = 40; //25 FPS, good performance vs. quality ratio
			refreshTimer.Start();

			//Allocate buffers
			gManager = BufferedGraphicsManager.Current;
			gManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
			gBuffer = gManager.Allocate(this.CreateGraphics(), this.ClientRectangle);
			gBufferBackground = gManager.Allocate(this.CreateGraphics(), this.ClientRectangle);
			gBufferScreen = gManager.Allocate(this.CreateGraphics(), this.ClientRectangle);
			gBackground = gBufferBackground.Graphics;
			gScreen = gBufferScreen.Graphics;
			g = gBuffer.Graphics;

			Clear();
		}
		#endregion

		#region Public properties
		/// <summary>
		/// Gets or sets a value indicating whether a dot grid should be displayed.
		/// </summary>
		public bool IsGridVisible
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets a value indicating whether cursor is visible.
		/// </summary>
		public bool IsCrossVisible
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets a value indicating whether tracker is visible.
		/// </summary>
		public bool IsTrackerVisible
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets a grid dot color.
		/// </summary>
		public Color GridColor
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets cursor color.
		/// </summary>
		public Color CrossColor
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets tracker line color.
		/// </summary>
		public Color TrackerColor
		{
			get;
			set;
		}

		/// <summary>
		/// Gets the current cursor position.
		/// </summary>
		public new NPoint Cursor
		{
			get
			{
				return this.cursor;
			}
		}
		private NPoint cursor;
		#endregion

		#region Overriden methods
		protected override void OnPaint(PaintEventArgs pe)
		{
			//Render from buffer to screen
			gBuffer.Render(pe.Graphics);
		}

		protected override void OnHandleDestroyed(EventArgs e)
		{
			gBuffer.Dispose();
			gManager.Dispose();
			base.OnHandleDestroyed(e);
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			//Teleport tool position to mouse click position
			this.Cursor.X = e.X;
			this.Cursor.Y = e.Y;

			//Clear garbage (render data from buffer)
			Render();

			//Draw cross on cursor location
			DrawCross();

			base.OnMouseDown(e);
		}
		#endregion

		#region Protected methods
		protected void DrawPixel(Color color, int x, int y)
		{
			//Draw pixel to buffer
			Bitmap pixel = new Bitmap(1, 1);
			pixel.SetPixel(0, 0, color);
			g.DrawImageUnscaled(pixel, x, y);
		}

		protected void DrawGrid()
		{
			//Distance between pixels
			int raster = 25;

			for (int x = 0; x < this.Width; x += raster)
			{
				for (int y = 0; y < this.Height; y += raster)
				{
					//Draw pixel to buffer
					DrawPixel(this.GridColor, x, y);
				}
			}
		}

		protected void DrawCross()
		{
			//Draw cross at the tool position
			DrawCross((int)this.Cursor.X, (int)this.Cursor.Y);
		}

		protected void DrawCross(int x, int y)
		{
			Pen pen = new Pen(this.CrossColor);
			Point center = new Point(x, y);

			//Cross radius
			int size = 3;

			//Draw on the screen
			Graphics dc = this.CreateGraphics();
			dc.DrawLine(pen, center, new Point(x + size, y));
			dc.DrawLine(pen, center, new Point(x - size, y));
			dc.DrawLine(pen, center, new Point(x, y + size));
			dc.DrawLine(pen, center, new Point(x, y - size));

			pen.Dispose();
		}

		protected void DrawTracker()
		{
			//Draw cursor tracking lines for the tool position
			DrawTracker((int)this.Cursor.X, (int)this.Cursor.Y);
		}

		protected void DrawTracker(int x, int y)
		{
			Pen pen = new Pen(this.TrackerColor);

			//Distance from center of cross
			//int dist = 5;

			//Draw on the screen
			Graphics dc = this.CreateGraphics();

			//Horizontal lines
			//dc.DrawLine(pen, new Point(0, y), new Point(x - dist, y));
			//dc.DrawLine(pen, new Point(x + dist, y), new Point(this.Width, y));

			//Vertical lines
			//dc.DrawLine(pen, new Point(x, 0), new Point(x, y - dist));
			//dc.DrawLine(pen, new Point(x, y + dist), new Point(x, this.Height));

			dc.DrawLine(pen, new Point(0, y), new Point(this.Width, y));
			dc.DrawLine(pen, new Point(x, 0), new Point(x, this.Height));

			pen.Dispose();
		}

		public void DrawAxes()
		{
			Graphics dc = this.CreateGraphics();
			Pen pen = new Pen(Color.Green);
			dc.DrawLine(pen, -250, 0, 250, 0);
			dc.DrawLine(pen, 0, -250, 0, 25);
		}

		public void DrawTool()
		{
			double radius = 2;

			double x = this.Cursor.X - radius / 2;
			double y = this.Cursor.Y - radius / 2;

			Color color = Color.Orange;
			if (this.Cursor.Z > 50) color = Color.Green; //High Z

			using (Brush brush = new SolidBrush(color))
			{
				g.FillEllipse(brush, new Rectangle((int)x, (int)y, (int)radius, (int)radius));
			}

			//Draw auxiliary graphics
			if (this.IsGridVisible) DrawGrid();
			if (this.IsCrossVisible) DrawCross();
			if (this.IsTrackerVisible) DrawTracker();
		}
		#endregion

		#region Public methods
		public void Step(int dx, int dy, int dz)
		{
			if (dx < 0) dx = -1;
			if (dx > 0) dx = 1;

			if (dy < 0) dy = -1;
			if (dy > 0) dy = 1;

			if (dz < 0) dz = -1;
			if (dz > 0) dz = 1;

			this.Cursor.X += dx;
			this.Cursor.Y += dy;
			this.Cursor.Z += dz;

			DrawTool();
		}

		public void Render()
		{
			//gScreen.FillRectangle(new SolidBrush(Color.FromArgb(0xAF, 0xAA, 0x99)), this.ClientRectangle);
			//gScreen.FillRectangle(new SolidBrush(Color.FromArgb(0xFF, 0xFF, 0xC0)), new Rectangle(10, 10, this.Width - 20, this.Height - 20));
			//gScreen.DrawRectangle(new Pen(Color.Black), new Rectangle(10, 10, this.Width - 20, this.Height - 20));
			//gBufferScreen.Render(this.CreateGraphics());
			gBuffer.Render(this.CreateGraphics());
		}

		public void Clear()
		{
			g.FillRectangle(new SolidBrush(Color.FromArgb(0xAF, 0xAA, 0x99)), this.ClientRectangle);
			g.FillRectangle(new SolidBrush(Color.FromArgb(0xFF, 0xFF, 0xC0)), new Rectangle(10, 10, this.Width - 20, this.Height - 20));
			g.DrawRectangle(new Pen(Color.Black), new Rectangle(10, 10, this.Width - 20, this.Height - 20));
			gBuffer.Render(this.CreateGraphics());
		}
		#endregion
	}
}
